using System;
using Glue.Converters;

namespace Glue.Internals
{
    internal class CollectionMapping : IMapBetweenCollections, IConverter
    {
        private readonly ICollectionMember p1;
        private readonly ICollectionMember p2;
        private readonly CreatorContainer bothCreators;
        
        public IConverter Converter { get; private set;}
        

        public CollectionMapping(ICollectionMember p1, ICollectionMember p2, IConverter converter, CreatorContainer creators)
        {
            p1.PassSpecialMappableCollections();
            p2.PassSpecialMappableCollections();
            this.p1 = p1;
            this.p2 = p2;
            bothCreators = creators;
            Converter = converter;
        }

        public object Map(object from, object to)
        {
            if (from == null)
                return null;

            AssertGlueCollection(from, p1);

            var fromCollection = (IGlueCollection)from;
            var toProperty = fromCollection.CreatedBy(p1) ? p2 : p1;

            Func<object, object> creator = GetCreator(toProperty);

            var toCollection = toProperty.CreateCollection(fromCollection);
            return CopyCollection(fromCollection,toCollection, creator);
        }

        private Func<object, object> GetCreator(ICollectionMember toMember)
        {
            return bothCreators.Get(toMember.GetElementType());
        }

        private void AssertGlueCollection(object value, ICollectionMember member)
        {
            if (!IsNullOrGlueCollection(value))
                throw (new GlueException("A CollectionProperty (representing: '" + member.Name + "') is not sending IGlueCollection objects in getter. Please report this bug."));
        }

        private bool IsNullOrGlueCollection(object value)
        {
            return value == null || value is IGlueCollection;
        }

        private object CopyCollection(IGlueCollection fromCollection, IGlueCollection toCollection, Func<object, object> creator)
        {
            while (fromCollection.MoveNext())
            {
                toCollection.Add(Converter.Map(fromCollection.Current,creator(fromCollection.Current)));
            }
            return toCollection;
        }

        public Type LeftType
        {
            get { return p1.MemberType; }
        }

        public Type RightType
        {
            get { return p2.MemberType; }
        }
    }
}